<?php
    class WZ_Inflector
    {
        protected static $_transliteration = array(
            '/ä|æ|ǽ/' => 'ae',
            '/ö|œ/' => 'oe',
            '/ü/' => 'ue',
            '/Ä/' => 'Ae',
            '/Ü/' => 'Ue',
            '/Ö/' => 'Oe',
            '/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ/' => 'A',
            '/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª/' => 'a',
            '/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
            '/ç|ć|ĉ|ċ|č/' => 'c',
            '/Ð|Ď|Đ/' => 'D',
            '/ð|ď|đ/' => 'd',
            '/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě/' => 'E',
            '/è|é|ê|ë|ē|ĕ|ė|ę|ě/' => 'e',
            '/Ĝ|Ğ|Ġ|Ģ/' => 'G',
            '/ĝ|ğ|ġ|ģ/' => 'g',
            '/Ĥ|Ħ/' => 'H',
            '/ĥ|ħ/' => 'h',
            '/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ/' => 'I',
            '/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı/' => 'i',
            '/Ĵ/' => 'J',
            '/ĵ/' => 'j',
            '/Ķ/' => 'K',
            '/ķ/' => 'k',
            '/Ĺ|Ļ|Ľ|Ŀ|Ł/' => 'L',
            '/ĺ|ļ|ľ|ŀ|ł/' => 'l',
            '/Ñ|Ń|Ņ|Ň/' => 'N',
            '/ñ|ń|ņ|ň|ŉ/' => 'n',
            '/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ/' => 'O',
            '/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º/' => 'o',
            '/Ŕ|Ŗ|Ř/' => 'R',
            '/ŕ|ŗ|ř/' => 'r',
            '/Ś|Ŝ|Ş|Š/' => 'S',
            '/ś|ŝ|ş|š|ſ/' => 's',
            '/Ţ|Ť|Ŧ/' => 'T',
            '/ţ|ť|ŧ/' => 't',
            '/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ/' => 'U',
            '/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ/' => 'u',
            '/Ý|Ÿ|Ŷ/' => 'Y',
            '/ý|ÿ|ŷ/' => 'y',
            '/Ŵ/' => 'W',
            '/ŵ/' => 'w',
            '/Ź|Ż|Ž/' => 'Z',
            '/ź|ż|ž/' => 'z',
            '/Æ|Ǽ/' => 'AE',
            '/ß/' => 'ss',
            '/Ĳ/' => 'IJ',
            '/ĳ/' => 'ij',
            '/Œ/' => 'OE',
            '/ƒ/' => 'f'
        );
        
        public static function encoding()
        {
            $config = \WZ::get('confApp')->confApp;
            return $config['app']['encoding'];
        }
        
        public static function htmlentities($value, $flags = ENT_QUOTES, $encoding = null, $double_encode = false)
        {
            static $already_cleaned = array();

            is_null($encoding) && $encoding = static::encoding();

            if (is_bool($value) || is_int($value) || is_float($value) || in_array($value, $already_cleaned, true)) {
                return $value;
            }

            if (is_string($value)) {
                $value = htmlentities($value, $flags, $encoding, $double_encode);
            } elseif (is_array($value) || ($value instanceof \Iterator && $value instanceof \ArrayAccess)) {
                is_object($value) && $already_cleaned[] = $value;

                foreach ($value as $k => $v) {
                    $value[$k] = static::htmlentities($v, $flags, $encoding, $double_encode);
                }
            } elseif ($value instanceof \Iterator || static::lower(get_class($value)) == 'stdclass') {
                $already_cleaned[] = $value;

                foreach ($value as $k => $v) {
                    $value->{$k} = static::htmlentities($v, $flags, $encoding, $double_encode);
                }
            } elseif (is_object($value)) {
                if ( ! method_exists($value, '__toString')) {
                    throw new \RuntimeException('Object class "' . get_class($value) . '" could not be converted to string or '.
                        'sanitized as ArrayAccess. Whitelist it in security.whitelisted_classes in app/config/config.php '.
                        'to allow it to be passed unchecked.');
                }

                $value = static::htmlentities((string) $value, $flags, $encoding, $double_encode);
            }

            return $value;
        }
        
        public static function length($value)
        {
            return (MB_STRING) ? mb_strlen($value, static::encoding()) : strlen($value);
        }
        
        public static function lower($value)
        {
            return (MB_STRING) ? mb_strtolower($value, static::encoding()) : strtolower($value);
        }
        
        public static function upper($value)
        {
            return (MB_STRING) ? mb_strtoupper($value, static::encoding()) : strtoupper($value);
        }
        
        public static function camelize($string, $spacify = true, $lazy = false)
        {
            if($spacify) {
                $repl = "str_replace('_', ' ', strtoupper('\\1'))";
            } else {
                $repl = "strtoupper('\\2')";
            }
            $ret = preg_replace("`(?<=[a-z0-9])(_([a-z0-9]))`e", $repl, $string);
            return str_replace(' ', '', $ret);
            if($lazy) {
                return lcfirst($ret);
            } else {
                return ucfirst($ret);
            }
        }
        
        public static function uncamelize($string, $splitter = "_") 
        {
            $string = preg_replace('/(?!^)[[:upper:]][[:lower:]]/', '$0', preg_replace('/(?!^)[[:upper:]]+/', $splitter . '$0', $string));
            return strtolower($string);
        }
        
        public static function humanize($lowerCaseAndUnderscoredWord) 
        {
            return ucwords(str_replace('_', ' ', $lowerCaseAndUnderscoredWord));
        }
        
        public static function slug($string, $replacement = '_') 
        {
            $quotedReplacement = preg_quote($replacement, '/');

            $merge = array(
                '/[^\s\p{Ll}\p{Lm}\p{Lo}\p{Lt}\p{Lu}\p{Nd}]/mu' => ' ',
                '/\\s+/' => $replacement,
                sprintf('/^[%s]+|[%s]+$/', $quotedReplacement, $quotedReplacement) => '',
            );

            $map = self::$_transliteration + $merge;
            return preg_replace(array_keys($map), array_values($map), $string);
        }
        
        public static function getStringAsOneLine($string) 
        {
            $string = str_replace("\r\n", " ", $string);
            $string = str_replace("\n", " ", $string);
            $string = str_replace("\r", " ", $string);
            $string = str_replace("\t", "", $string);
            $string = preg_replace ('#[ ]+#', ' ', $string);
            return $string;
        }
        
        public static function cutStringRespectingWhitespace($string, $length) 
        {
            if ($length < strlen($string)) {
                $text = substr($string, 0, $length);
                if (false !== ($length = strrpos($text, ' '))) {
                    $text = substr($text, 0, $length);
                }
                $string = $text . "...";
            }
            return $string;
        }
        
        public static function html($string, $options = array()) 
        {
            $default = array(
                'remove' => false,
                'quotes' => ENT_QUOTES,
                'double' => true
            );

            $options = array_merge($default, $options);

            if (!isset($options['charset'])) {
                $options['charset'] = 'UTF-8';
            }
            if ($options['remove']) {
                $string = strip_tags($string);
            }

            return htmlentities($string, $options['quotes'], $options['charset'], $options['double']);
        }
        
        public static function stripWhitespace($string) 
        {
            $r = preg_replace('/[\n\r\t]+/', '', $string);
            return preg_replace('/\s{2,}/u', ' ', $r);
        }
        
        public static function stripImages($string) 
        {
            $string = preg_replace('/(<a[^>]*>)(<img[^>]+alt=")([^"]*)("[^>]*>)(<\/a>)/i', '$1$3$5<br />', $string);
            $string = preg_replace('/(<img[^>]+alt=")([^"]*)("[^>]*>)/i', '$2<br />', $string);
            $string = preg_replace('/<img[^>]*>/i', '', $string);
            return $string;
        }

        public static function stripScripts($string) 
        {
            return preg_replace('/(<link[^>]+rel="[^"]*stylesheet"[^>]*>|<img[^>]*>|style="[^"]*")|<script[^>]*>.*?<\/script>|<style[^>]*>.*?<\/style>|<!--.*?-->/is', '', $string);
        }
        
        public static function stripAll($string) 
        {
            $string = WZ_Inflector::stripWhitespace($string);
            $string = WZ_Inflector::stripImages($string);
            $string = WZ_Inflector::stripScripts($string);
            return $string;
        }
        
        public static function stripTags($string) 
        {
            $params = func_get_args();

            for ($i = 1, $count = count($params) ; $i < $count ; $i++) {
                $string = preg_replace('/<' . $params[$i] . '\b[^>]*>/i', '', $string);
                $string = preg_replace('/<\/' . $params[$i] . '[^>]*>/i', '', $string);
            }
            return $string;
        }
        
        public static function insert($string, $data, $options = array()) 
        {
            $defaults = array(
                'before' => ':', 'after' => null, 'escape' => '\\', 'format' => null, 'clean' => false
            );
            $options += $defaults;
            $format = $options['format'];
            $data = (array)$data;
            if (empty($data)) {
                return ($options['clean']) ? WZ_Inflector::cleanInsert($string, $options) : $string;
            }

            if (!isset($format)) {
                $format = sprintf(
                    '/(?<!%s)%s%%s%s/',
                    preg_quote($options['escape'], '/'),
                    str_replace('%', '%%', preg_quote($options['before'], '/')),
                    str_replace('%', '%%', preg_quote($options['after'], '/'))
                );
            }

            if (strpos($string, '?') !== false && is_numeric(key($data))) {
                $offset = 0;
                while (($pos = strpos($string, '?', $offset)) !== false) {
                    $val = array_shift($data);
                    $offset = $pos + strlen($val);
                    $string = substr_replace($string, $val, $pos, 1);
                }
                return ($options['clean']) ? WZ_Inflector::cleanInsert($string, $options) : $string;
            } else {
                asort($data);

                $hashKeys = array();
                foreach ($data as $key => $value) {
                    $hashKeys[] = crc32($key);
                }

                $tempData = array_combine(array_keys($data), array_values($hashKeys));
                krsort($tempData);
                foreach ($tempData as $key => $hashVal) {
                    $key = sprintf($format, preg_quote($key, '/'));
                    $string = preg_replace($key, $hashVal, $string);
                }
                $dataReplacements = array_combine($hashKeys, array_values($data));
                foreach ($dataReplacements as $tmpHash => $tmpValue) {
                    $tmpValue = (is_array($tmpValue)) ? '' : $tmpValue;
                    $string = str_replace($tmpHash, $tmpValue, $string);
                }
            }

            if (!isset($options['format']) && isset($options['before'])) {
                $string = str_replace($options['escape'] . $options['before'], $options['before'], $string);
            }
            return ($options['clean']) ? WZ_Inflector::cleanInsert($string, $options) : $string;
        }
        
        public static function cleanInsert($str, $options) 
        {
            $clean = $options['clean'];
            if (!$clean) {
                return $str;
            }
            if ($clean === true) {
                $clean = array('method' => 'text');
            }
            if (!is_array($clean)) {
                $clean = array('method' => $options['clean']);
            }
            switch ($clean['method']) {
                case 'html':
                    $clean = array_merge(array(
                        'word' => '[\w,.]+',
                        'andText' => true,
                        'replacement' => '',
                    ), $clean);
                    $kleenex = sprintf(
                        '/[\s]*[a-z]+=(")(%s%s%s[\s]*)+\\1/i',
                        preg_quote($options['before'], '/'),
                        $clean['word'],
                        preg_quote($options['after'], '/')
                    );
                    $str = preg_replace($kleenex, $clean['replacement'], $str);
                    if ($clean['andText']) {
                        $options['clean'] = array('method' => 'text');
                        $str = WZ_Inflector::cleanInsert($str, $options);
                    }
                    break;
                case 'text':
                    $clean = array_merge(array(
                        'word' => '[\w,.]+',
                        'gap' => '[\s]*(?:(?:and|or)[\s]*)?',
                        'replacement' => '',
                    ), $clean);

                    $kleenex = sprintf(
                        '/(%s%s%s%s|%s%s%s%s)/',
                        preg_quote($options['before'], '/'),
                        $clean['word'],
                        preg_quote($options['after'], '/'),
                        $clean['gap'],
                        $clean['gap'],
                        preg_quote($options['before'], '/'),
                        $clean['word'],
                        preg_quote($options['after'], '/')
                    );
                    $str = preg_replace($kleenex, $clean['replacement'], $str);
                    break;
            }
            return $str;
        }
        
        public static function clean($data)
        {
            if (is_array($data)) {
                foreach ($data as $key => $value) {
                    unset($data[$key]);
                    $data[self::clean($key)] = self::clean($value);
                }
            } else {
                $data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
            }
            return $data;
        }
        
        public static function bbcode($string)
        {
            $bbcode = \Zend_Markup::factory('Bbcode');
            $string = $bbcode->render($string);
     
            return $string;
        }
    }